﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.IO;
using System.Xml;
using System.Web;
using System.Web.Mvc;
using System.ServiceModel.Syndication;
using LevenBlog.Core.Data;
using LevenBlog.Core.Entities;
using LevenBlog.Core.Utils;
using LevenBlog.Core.Service;
using LevenBlog.Core.Cache;

namespace LevenBlog.Core.Mvc
{
    public class TrackResponseResult : ActionResult
    {
        private static IList<Pair<string, DateTime>> trackCache = new List<Pair<string, DateTime>>();
        private static readonly int trackTime = 5;

        public int BlogID { get; private set; }
        public IDbProvider Provider { get; set; }
        public TrackType Type { get; private set; }

        public TrackResponseResult(int blogID, TrackType type)
        {
            BlogID = blogID;
            Type = type;
        }

        public override void ExecuteResult(ControllerContext context)
        {
            HttpRequestBase request = context.HttpContext.Request;
            HttpResponseBase response = context.HttpContext.Response;
            //UrlHelper urlHelper = new UrlHelper(new ViewContext(context, "track", string.Empty, (context.Controller as Controller).ViewData, (context.Controller as Controller).TempData));
            //UrlHelper urlHelper = new UrlHelper(new ViewContext(context, "track", context.Controller.ViewData, context.Controller.TempData));
            UrlHelper urlHelper = (context.Controller as Controller).Url;
            response.ContentType = "text/xml;charset=utf-8";
            if (Type == TrackType.Post)
            {
                if (!CheckTrack(Fetch.UserIp))
                {
                    List<string> errors = new List<string>();
                    errors.Add(string.Format("每个用户在{0}分钟内只允许请求trackback一次.", trackTime));
                    response.Write(CreateResponse(errors.Count, errors, urlHelper));
                    return;
                }
                else
                {
                    string title = request.Form["title"];
                    string excerpt = request.Form["excerpt"];
                    string url = request.Form["url"];
                    string blog_name = request.Form["blog_name"];
                    url = url == null ? string.Empty : url.HtmlEncode();
                    if (string.IsNullOrEmpty(url))
                    {
                        List<string> errors = new List<string>();
                        errors.Add("引用URL不能为空.");
                        response.Write(CreateResponse(errors.Count, errors, urlHelper));
                        return;
                    }
                    title = title == null ? url : title.HtmlEncode();
                    excerpt = excerpt == null ? string.Empty : excerpt.HtmlEncode();
                    blog_name = blog_name == null ? string.Empty : blog_name.HtmlEncode();
                    lb_blog blog = Blog.Get(Provider, BlogID);
                    if (blog == null)
                    {
                        List<string> errors = new List<string>();
                        errors.Add("指定的Blog没有找到.");
                        response.Write(CreateResponse(errors.Count, errors, urlHelper));
                        return;
                    }
                    lb_track track = new lb_track();
                    track.blogid = blog.id;
                    track.intro = excerpt;
                    track.ip = Fetch.UserIp;
                    track.posttime = DateTime.Now;
                    track.site = blog_name;
                    track.siteurl = url;
                    track.title = title;
                    track.url = url;
                    Track.Insert(Provider, track);
                    List<string> messages = new List<string>();
                    messages.Add("添加新的引用成功.");
                    ObjectCache.RegexRemove(".*blog.*");
                    ObjectCache.RegexRemove(".*track.*");
                    response.Write(CreateResponse(messages.Count, messages, urlHelper));
                }
            }
            else
            {
                response.Write(CreateAnswer(Track.GetFromBlogID(Provider, BlogID), urlHelper, request));
            }
        }

        protected string CreateResponse(int errorNum, IList<string> messageList,UrlHelper urlHelper)
        {
            StringBuilder sb = new StringBuilder();
            sb.AppendLine("<?xml version=\"1.0\" encoding=\"utf-8\"?>");
            sb.AppendFormat("<?xml-stylesheet type=\"text/xsl\" href=\"{0}\"?>\r\n", urlHelper.RouteUrl(new { controller = "blog", action = "trackbackxsl" }));
            sb.AppendLine("<response>");
            sb.AppendFormat("<error>{0}</error>\r\n", errorNum);
            foreach (string e in messageList)
            {
                sb.AppendFormat("<message>{0}</message>\r\n", e);
            }
            sb.AppendLine("</response>");
            return sb.ToString();
        }

        protected string CreateAnswerItem(IList<lb_track> tracks,UrlHelper urlHelper,HttpRequestBase request)
        {
            if (tracks == null)
                return null;
            if (tracks.Count < 1)
                return null;
            List<SyndicationItem> syItemList = new List<SyndicationItem>();
            foreach (lb_track track in tracks)
            {
                SyndicationItem item = new SyndicationItem();
                item.Title = new TextSyndicationContent(track.title);
                item.AddPermalink(new Uri(request.Url.AbsoluteUri + urlHelper.RouteUrl(new { controller = "blog", action = "view", id = track.id.ToString() })));
                item.Authors.Add(new SyndicationPerson(track.site, track.site, track.siteurl));
                item.Content = new TextSyndicationContent(track.intro);
                item.Id = track.id.ToString();
                item.PublishDate = track.posttime;
                syItemList.Add(item);
            }
            Rss20ItemFormatter rssItem = new Rss20ItemFormatter(syItemList[0]);
            MemoryStream ms = new MemoryStream();
            XmlWriter xw = XmlWriter.Create(ms);
            rssItem.WriteTo(xw);
            xw.Flush();
            ms.Seek(0, SeekOrigin.Begin);
            StreamReader sr = new StreamReader(ms, Encoding.UTF8);
            string result = sr.ReadToEnd();
            ms.Close();
            result = Regex.Replace(result, "<\\?xml.*\\?>", string.Empty);
            return result;
        }

        protected string CreateAnswer(IList<lb_track> tracks, UrlHelper urlHelper, HttpRequestBase request)
        {
            int num = tracks == null ? 0 : tracks.Count;
            if (num < 1)
            {
                List<string> errors = new List<string>();
                errors.Add("日志没有被引用.");
                return CreateResponse(1, errors, urlHelper);
            }
            StringBuilder sb = new StringBuilder();
            sb.AppendLine("<?xml version=\"1.0\" encoding=\"utf-8\" ?>");
            sb.AppendFormat("<?xml-stylesheet type=\"text/xsl\" href=\"{0}\"?>\r\n", urlHelper.RouteUrl(new { controller = "blog", action = "trackbackxsl" }));
            sb.AppendLine("<response>");
            sb.AppendLine("<error>0</error>");
            sb.AppendLine("<rss version=\"0.91\">");
            sb.AppendLine("<channel>");
            sb.AppendLine(string.Format("<title>{0}</title>", Config.Get(Provider).title.HtmlEncode()));
            sb.AppendFormat("<link>{0}{1}</link>\r\n", request.Url.AbsoluteUri, urlHelper.RouteUrl(new { controller = "blog", action = "track", id = BlogID.ToString() }));
            sb.AppendFormat("<description>{0}</description>\r\n", Config.Get(Provider).intro.HtmlEncode());
            sb.AppendLine("<language>zh-cn</language>");
            sb.AppendLine(CreateAnswerItem(tracks, urlHelper, request));
            sb.AppendLine("</rss>");
            sb.AppendLine("</response>");
            return sb.ToString();
        }

        private static bool CheckTrack(string ip)
        {
            Pair<string, DateTime> find = null;
            bool result = true;
            try
            {
                find = trackCache.Single(tc => tc.First == ip);
                DateTime now = DateTime.Now;
                if (((TimeSpan)(now - find.Second)).TotalMinutes <= trackTime)
                    result = false;
                find.Second = now;
            }
            catch
            {
                find = new Pair<string, DateTime>(ip, DateTime.Now);
                if (trackCache.Count > 100)
                {
                    trackCache.Clear();
                }
                trackCache.Add(find);
            }
            return result;
        }
    }

    public enum TrackType
    {
        View,
        Post
    }
}
